home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
news
/
inn1.000
/
inn1.4sec-linux-src.tar
/
inn
/
nnrpd
/
nnrpd.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-03-18
|
17KB
|
676 lines
/* $Revision: 1.18 $
**
** NNTP server for readers (NNRP) for InterNetNews.
** This server doesn't do any real load-limiting, except for what has
** proven empirically necesary (i.e., look at GRPscandir).
*/
#define MAINLINE
#include <signal.h>
#include "nnrpd.h"
#include <sys/time.h>
#include <netdb.h>
#if defined(HPUX)
#include <sys/pstat.h>
#endif /* defined(HPUX) */
#define CMDany -1
typedef struct _CMDENT {
STRING Name;
FUNCPTR Function;
BOOL Needauth;
int Minac;
int Maxac;
STRING Help;
} CMDENT;
char NOACCESS[] = NNTP_ACCESS;
char ACTIVE[] = _PATH_ACTIVE;
char ACTIVETIMES[] = _PATH_ACTIVETIMES;
char HISTORY[] = _PATH_HISTORY;
char NEWSGROUPS[] = _PATH_NEWSGROUPS;
/* Default permission -- change with adb. */
BOOL PERMdefault = FALSE;
STATIC double STATstart;
STATIC double STATfinish;
#if !defined(HPUX)
STATIC char *TITLEstart;
STATIC char *TITLEend;
#endif /* !defined(HPUX) */
STATIC SIGVAR ChangeTrace;
extern FUNCTYPE CMDauthinfo();
extern FUNCTYPE CMDdate();
extern FUNCTYPE CMDfetch();
extern FUNCTYPE CMDgroup();
STATIC FUNCTYPE CMDhelp();
extern FUNCTYPE CMDlist();
extern FUNCTYPE CMDmode();
extern FUNCTYPE CMDnewgroups();
extern FUNCTYPE CMDnewnews();
extern FUNCTYPE CMDnextlast();
extern FUNCTYPE CMDpost();
extern FUNCTYPE CMDxgtitle();
extern FUNCTYPE CMDxhdr();
extern FUNCTYPE CMDxover();
extern FUNCTYPE CMDxpat();
extern FUNCTYPE CMDxpath();
extern FUNCTYPE CMD_unimp();
#if defined(DO_DO_XTHREAD)
extern FUNCTYPE CMDxthread();
#endif /* defined(DO_DO_XTHREAD) */
STATIC char CMDfetchhelp[] = "[MessageID|Number]";
STATIC CMDENT CMDtable[] = {
{ "authinfo", CMDauthinfo, FALSE, 3, 3,
"user Name|pass Password" },
{ "article", CMDfetch, FALSE, 1, 2,
CMDfetchhelp },
{ "body", CMDfetch, FALSE, 1, 2,
CMDfetchhelp },
{ "date", CMDdate, FALSE, 1, 1,
NULL },
{ "group", CMDgroup, FALSE, 2, 2,
"newsgroup" },
{ "head", CMDfetch, FALSE, 1, 2,
CMDfetchhelp },
{ "help", CMDhelp, FALSE, 1, CMDany,
NULL },
{ "ihave", CMD_unimp, TRUE, 1, 2,
NULL },
{ "last", CMDnextlast, FALSE, 1, 1,
NULL },
{ "list", CMDlist, FALSE, 1, 2,
"[active|newsgroups|distributions|schema]" },
{ "listgroup", CMDgroup, FALSE, 1, 2,
"newsgroup" },
{ "mode", CMDmode, FALSE, 2, 2,
"reader" },
{ "newgroups", CMDnewgroups, FALSE, 3, 5,
"yymmdd hhmmss [\"GMT\"] [<distributions>]" },
{ "newnews", CMDnewnews, FALSE, 4, 6,
"newsgroups yymmdd hhmmss [\"GMT\"] [<distributions>]" },
{ "next", CMDnextlast, FALSE, 1, 1,
NULL },
{ "post", CMDpost, TRUE, 1, 1,
NULL },
{ "slave", CMD_unimp, FALSE, 1, 1,
NULL },
{ "stat", CMDfetch, FALSE, 1, 2,
CMDfetchhelp },
{ "xgtitle", CMDxgtitle, FALSE, 1, 2,
"[group_pattern]" },
{ "xhdr", CMDxhdr, FALSE, 2, 3,
"header [range|MessageID]" },
{ "xover", CMDxover, FALSE, 1, 2,
"[range]" },
{ "xpat", CMDxpat, FALSE, 4, CMDany,
"header range|MessageID pat [morepat...]" },
{ "xpath", CMDxpath, FALSE, 2, 2,
"xpath MessageID" },
#if defined(DO_DO_XTHREAD)
{ "xthread", CMDxthread, FALSE, 1, 2,
"[dbinit|thread]" },
#endif /* defined(DO_DO_XTHREAD) */
{ NULL }
};
/*
** Log a summary status message and exit.
*/
NORETURN
ExitWithStats(x)
int x;
{
TIMEINFO Now;
double usertime;
double systime;
(void)fflush(stdout);
(void)GetTimeInfo(&Now);
STATfinish = TIMEINFOasDOUBLE(Now);
if (GetResourceUsage(&usertime, &systime) < 0) {
usertime = 0;
systime = 0;
}
GRPreport();
syslog(L_NOTICE, "%s exit articles %ld groups %ld",
ClientHost, ARTcount, GRPcount);
if (POSTreceived || POSTrejected)
syslog(L_NOTICE, "%s posts received %ld rejected %ld",
ClientHost, POSTreceived, POSTrejected);
syslog(L_NOTICE, "%s times user %.3f system %.3f elapsed %.3f",
ClientHost, usertime, systime, STATfinish - STATstart);
exit(x);
}
/*
** The "help" command.
*/
/* ARGSUSED0 */
STATIC FUNCTYPE
CMDhelp(ac, av)
int ac;
char *av[];
{
CMDENT *cp;
Reply("%s\r\n", NNTP_HELP_FOLLOWS);
for (cp = CMDtable; cp->Name; cp++)
if (cp->Help == NULL)
Printf(" %s\r\n", cp->Name);
else
Printf(" %s %s\r\n", cp->Name, cp->Help);
Printf("Report problems to <%s@%s>\r\n",
NEWSMASTER, GetConfigValue(_CONF_FROMHOST));
Reply(".\r\n");
}
/*
** Unimplemented catch-all.
*/
/* ARGSUSED0 */
FUNCTYPE
CMD_unimp(ac, av)
int ac;
char *av[];
{
if (caseEQ(av[0], "ihave"))
Reply("%d Transfer permission denied\r\n", NNTP_AUTH_NEEDED_VAL);
else if (caseEQ(av[0], "slave"))
/* Somebody sends us this? I don't believe it! */
Reply("%d Unsupported\r\n", NNTP_SLAVEOK_VAL);
else
Reply("%d %s not implemented; try help\r\n",
NNTP_BAD_COMMAND_VAL, av[0]);
}
/*
** Overwrite the original argv so that ps will show what's going on.
*/
STATIC void
TITLEset(what)
char *what;
{
#if !defined(HPUX)
register char *p;
register int i;
char buff[BUFSIZ];
/* Make ps think we're swapped out so we get "(nnrpd)" in the output. */
p = TITLEstart;
*p++ = '-';
(void)sprintf(buff, "%s %s", ClientHost, what);
i = strlen(buff);
if (i > TITLEend - p - 2) {
i = TITLEend - p - 2;
buff[i] = '\0';
}
(void)strcpy(p, buff);
for (p += i; p < TITLEend; )
*p++ = ' ';
#else
char buff[BUFSIZ];
(void)sprintf(buff, "(nnrpd) %s %s", ClientHost, what);
(void)pstat(PSTAT_SETCMD, buff, 0, 0, 0);
#endif /* defined(HPUX) */
}
#if defined(DO_NNRP_GETHOSTBYADDR)
/*
** Convert an IP address to a hostname. Don't trust the reverse lookup,
** since anyone can fake .in-addr.arpa entries.
*/
STATIC BOOL
Address2Name(ap, hostname, i)
register INADDR *ap;
register char *hostname;
register int i;
{
register char *p;
register struct hostent *hp;
#if defined(h_addr)
register char **pp;
#endif
/* Get the official hostname, store it away. */
if ((hp = gethostbyaddr((char *)ap, sizeof *ap, AF_INET)) == NULL)
return FALSE;
(void)strncpy(hostname, hp->h_name, i);
hostname[i - 1] = '\0';
/* Get addresses for this host. */
if ((hp = gethostbyname(hostname)) == NULL)
return FALSE;
/* Make sure one of those addresses is the address we got. */
#if defined(h_addr)
/* We have many addresses */
for (pp = hp->h_addr_list; *pp; pp++)
if (memcmp((POINTER)&ap->s_addr, (POINTER)*pp,
(SIZE_T)hp->h_length) == 0)
break;
if (*pp == NULL)
return FALSE;
#else
/* We have one address. */
if (memcmp((POINTER)&ap->s_addr, (POINTER)hp->h_addr,
(SIZE_T)hp->h_length) != 0)
return FALSE;
#endif
/* Only needed for misconfigured YP/NIS systems. */
if (strchr(hostname, '.') == NULL
&& (p = GetConfigValue(_CONF_DOMAIN)) != NULL) {
(void)strcat(hostname, ".");
(void)strcat(hostname, p);
}
/* Make all lowercase, for wildmat. */
for (p = hostname; *p; p++)
if (CTYPE(isupper, *p))
*p = tolower(*p);
return TRUE;
}
#endif /* defined(DO_NNRP_GETHOSTBYADDR) */
BOOL
PERMinfile(hp, ip, user, pass, accesslist)
char *hp;
char *ip;
char *user;
char *pass;
char *accesslist;
{
static char ACCESS[] = _PATH_NNRPACCESS;
register FILE *F;
register char *p;
register BOOL found;
register int i;
char buff[BIG_BUFFER];
char *fields[5];
if ((F = fopen(ACCESS, "r")) == NULL) {
syslog(L_ERROR, "%s cant fopen %s %m", ClientHost, ACCESS);
return FALSE;
}
PERMcanread = FALSE;
PERMcanpost = FALSE;
found = FALSE;
accesslist[0] = '\0';
while (fgets(buff, sizeof buff, F) != NULL) {
if ((p = strchr(buff, '\n')) != NULL)
*p = '\0';
if ((p = strchr(buff, COMMENT_CHAR)) != NULL)
*p = '\0';
if (buff[0] == '\0')
continue;
/* Split "host:permissions:user:pass:groups" into fields. */
for (fields[0] = buff, i = 0, p = buff; *p; p++)
if (*p == ':') {
*p = '\0';
fields[++i] = p + 1;
}
if (i != 4)
/* Malformed line. */
continue;
if (hp)
/* Got an address; try to match either the IP address or as
* a text hostname. */
if (!(ip && wildmat(ip, fields[0])) && !wildmat(hp, fields[0]))
continue;
/* Matching for a specific user or just the host? */
if (user && (!EQ(user, fields[2]) || !EQ(pass, fields[3])))
continue;
PERMcanread = strchr(fields[1], 'R') != NULL;
PERMcanpost = strchr(fields[1], 'P') != NULL;
(void)strcpy(PERMuser, fields[2]);
(void)strcpy(PERMpass, fields[3]);
(void)strcpy(accesslist, fields[4]);
found = TRUE;
}
(void)fclose(F);
return found;
}
/*
** Determine access rights of the client.
*/
STATIC void
StartConnection(accesslist)
char *accesslist;
{
struct sockaddr_in sin;
int length;
char buff[SMBUF];
char *ClientAddr;
/* Get the peer's name. */
length = sizeof sin;
ClientAddr = NULL;
if (getpeername(STDIN, (struct sockaddr *)&sin, &length) < 0) {
if (!isatty(STDIN)) {
syslog(L_ERROR, "%s cant getpeername %m", "?");
Printf("%d I can't get your name. Goodbye.\r\n", NNTP_ACCESS_VAL);
ExitWithStats(1);
}
(void)strcpy(ClientHost, "stdin");
}
else {
if (sin.sin_family != AF_INET) {
syslog(L_ERROR, "%s bad_address_family %ld",
"?", (long)sin.sin_family);
Printf("%d Bad address family. Goodbye.\r\n", NNTP_ACCESS_VAL);
ExitWithStats(1);
}
/* Get client's name. */
#if defined(DO_NNRP_GETHOSTBYADDR)
if (!Address2Name(&sin.sin_addr, ClientHost, sizeof ClientHost)) {
(void)strcpy(ClientHost, inet_ntoa(sin.sin_addr));
syslog(L_ERROR, "? cant gethostbyaddr %s %m", ClientHost);
}
else {
ClientAddr = buff;
(void)strcpy(buff, inet_ntoa(sin.sin_addr));
}
#else
(void)strcpy(ClientHost, inet_ntoa(sin.sin_addr));
#endif /* defined(DO_NNRP_GETHOSTBYADDR) */
}
syslog(L_NOTICE, "%s connect", ClientHost);
if (!PERMinfile(ClientHost, ClientAddr, (char *)NULL, (char *)NULL,
accesslist)) {
syslog(L_NOTICE, "%s no_access", ClientHost);
Printf("%d You are not in my access file. Goodbye.\r\n",
NNTP_ACCESS_VAL);
ExitWithStats(1);
}
}
#if !defined(VAR_NONE)
#if defined(VAR_VARARGS)
#if defined(lint)
#define START_VARARG(fmt, vp, type) va_start(vp); fmt = NULL
#else
#define START_VARARG(fmt, vp, type) va_start(vp); fmt = va_arg(vp, type)
#endif /* defined(lint) */
#endif /* defined(VAR_VARARGS) */
#if defined(VAR_STDARGS)
#define START_VARARG(fmt, vp, type) va_start(vp, fmt)
#endif /* defined(VAR_STDARGS) */
/*
** Send a reply, possibly with debugging output.
*/
/*VARARGS*/
void
#if defined(VAR_VARARGS)
Reply(va_alist)
va_dcl
#endif /* defined(VAR_VARARGS) */
#if defined(VAR_STDARGS)
Reply(char *fmt, ...)
#endif /* defined(VAR_STDARGS) */
{
register int oerrno;
register char *p;
va_list vp;
char buff[2048];
#if defined(VAR_VARARGS)
register char *fmt;
#endif /* defined(VAR_VARARGS) */
START_VARARG(fmt, vp, char*);
(void)vprintf(fmt, vp);
va_end(vp);
if (Tracing) {
oerrno = errno;
START_VARARG(fmt, vp, char*);
/* Copy output, but strip trailing CR-LF. */
(void)vsprintf(buff, fmt, vp);
p = buff + strlen(buff) - 1;
while (p >= buff && (*p == '\n' || *p == '\r'))
*p-- = '\0';
syslog(LOG_DEBUG, "%s > %s", ClientHost, buff);
va_end(vp);
errno = oerrno;
}
}
#endif /* !defined(VAR_NONE) */
/*
** Got a signal; toggle tracing.
*/
STATIC SIGHANDLER
ToggleTrace(s)
int s;
{
ChangeTrace = TRUE;
(void)signal(s, ToggleTrace);
}
/*
** Print a usage message and exit.
*/
STATIC void
Usage()
{
(void)fprintf(stderr, "Usage error.\n");
exit(1);
}
/* ARGSUSED0 */
int
main(argc, argv)
int argc;
char *argv[];
{
#if NNRP_LOADLIMIT > 0
int load;
#endif /* NNRP_LOADLIMIT > 0 */
CMDENT *cp;
char buff[NNTP_STRLEN];
char **av;
int ac;
READTYPE r;
TIMEINFO Now;
register int i;
char *Reject;
char accesslist[BIG_BUFFER];
#if !defined(HPUX)
/* Save start and extent of argv for TITLEset. */
TITLEstart = argv[0];
TITLEend = argv[argc - 1] + strlen(argv[argc - 1]) - 1;
#endif /* !defined(HPUX) */
/* Parse arguments. Must COPY() optarg if used because the
* TITLEset() routine would clobber it! */
Reject = NULL;
while ((i = getopt(argc, argv, "S:r:s:t")) != EOF)
switch (i) {
default:
Usage();
/* NOTREACHED */
case 'S': /* We're a slave to NNTP master */
RemoteMaster = COPY(optarg);
break;
case 's': /* Unused title string */
break;
case 't': /* Tracing */
Tracing = TRUE;
break;
case 'r': /* Reject connection message */
Reject = COPY(optarg);
break;
}
argc -= optind;
if (argc)
Usage();
/* Setup. */
openlog("nnrpd", L_OPENLOG_FLAGS | LOG_PID, LOG_INN_PROG);
if (GetTimeInfo(&Now) < 0) {
syslog(L_FATAL, "cant gettimeinfo %m");
exit(1);
}
STATstart = TIMEINFOasDOUBLE(Now);
if ((MyHostName = GetConfigValue(_CONF_PATHHOST)) == NULL) {
syslog(L_FATAL, "cant getconfigvalue %m");
ExitWithStats(1);
}
MyHostName = COPY(MyHostName);
#if NNRP_LOADLIMIT > 0
if ((load = GetLoadAverage()) > NNRP_LOADLIMIT) {
syslog(L_NOTICE, "load %d > %d", load, NNRP_LOADLIMIT);
Reply("%d load at %d, try later\r\n", NNTP_GOODBYE_VAL, load);
ExitWithStats(1);
}
#endif /* NNRP_LOADLIMIT > 0 */
/* Ignore SIGPIPE, since we'll see closed connections with read. */
(void)signal(SIGPIPE, SIG_IGN);
/* Arrange to toggle tracing. */
(void)signal(SIGHUP, ToggleTrace);
/* Get permissions and see if we can talk to this client */
StartConnection(accesslist);
if (!PERMcanread && !PERMcanpost) {
syslog(L_NOTICE, "%s no_permission", ClientHost);
Printf("%d You have no permission to talk. Goodbye.\r\n",
NNTP_ACCESS_VAL);
ExitWithStats(1);
}
/* Proceed with initialization. */
PERMneedauth = PERMuser[0] != '\0' && PERMpass != '\0';
PERMspecified = NGgetlist(&PERMlist, accesslist);
TITLEset("connect");
/* Were we told to reject connections? */
if (Reject) {
syslog(L_NOTICE, "%s rejected %s", ClientHost, Reject);
Reply("%s %s\r\n", NNTP_GOODBYE, Reject);
ExitWithStats(0);
}
ARTreadschema();
if (!GetGroupList()) {
/* This shouldn't really happen. */
syslog(L_NOTICE, "%s cant getgrouplist %m", ClientHost);
Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
ExitWithStats(1);
}
Reply("%d %s InterNetNews NNRP server %s ready (%s).\r\n",
PERMcanpost ? NNTP_POSTOK_VAL : NNTP_NOPOSTOK_VAL,
MyHostName, INNVersion(),
PERMcanpost ? "posting ok" : "no posting");
/* Main dispatch loop. */
for (av = NULL; ; ) {
(void)fflush(stdout);
if (ChangeTrace) {
Tracing = Tracing ? FALSE : TRUE;
syslog(L_TRACE, "trace %sabled", Tracing ? "en" : "dis");
ChangeTrace = FALSE;
}
switch (r = READline(buff, (int)sizeof buff, CLIENT_TIMEOUT)) {
default:
syslog(L_ERROR, "%s internal %d in main", ClientHost, r);
/* FALLTHROUGH */
case RTtimeout:
syslog(L_NOTICE, "%s timeout", ClientHost);
Printf("%d Timeout after %d seconds, closing connection.\r\n",
NNTP_TEMPERR_VAL, CLIENT_TIMEOUT);
ExitWithStats(1);
break;
case RTlong:
Reply("%d Line too long\r\n", NNTP_BAD_COMMAND_VAL);
continue;
case RTok:
/* Do some input processing, check for blank line. */
if (buff[0] == '\0')
continue;
if (Tracing)
syslog(L_TRACE, "%s < %s", ClientHost, buff);
ac = Argify(buff, &av);
if (ac == 0)
continue;
break;
case RTeof:
/* Handled below. */
break;
}
/* Client gone? */
if (r == RTeof || caseEQ(av[0], "quit"))
break;
/* Find command. */
for (cp = CMDtable; cp->Name; cp++)
if (caseEQ(cp->Name, av[0]))
break;
if (cp->Name == NULL) {
if ((int)strlen(buff) > 40)
syslog(L_NOTICE, "%s unrecognized %.40s...", ClientHost, buff);
else
syslog(L_NOTICE, "%s unrecognized %s", ClientHost, buff);
Reply("%d What?\r\n", NNTP_BAD_COMMAND_VAL);
continue;
}
/* Check usage. */
if ((cp->Minac != CMDany && ac < cp->Minac)
|| (cp->Maxac != CMDany && ac > cp->Maxac)) {
Reply("%d %s\r\n",
NNTP_SYNTAX_VAL, cp->Help ? cp->Help : "Usage error");
continue;
}
/* Check permissions and dispatch. */
if (cp->Needauth && PERMneedauth) {
Reply("%d Authentication required for command\r\n",
NNTP_AUTH_NEEDED_VAL);
continue;
}
TITLEset(av[0]);
(*cp->Function)(ac, av);
}
Reply("%s\r\n", NNTP_GOODBYE_ACK);
ExitWithStats(0);
/* NOTREACHED */
}